﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BingVis.SilverlightClient.Common;
using BingVis.SilverlightClient.GoogleTransit;
using System.IO;
using System.ComponentModel;
using System.Windows;
using System.Threading;
using System.Diagnostics;
using System.Windows.Input;
using Microsoft.Win32;
using System.Windows.Controls;


namespace BingVis.SilverlightClient
{

    public class MainShellViewModel : ViewModelBase
    {
        private const string BASIC_HELP =
@"
MBTA Transit Stops are shown in the left hand side of 
the screen.  
- Stops with $ indicate stops that show fares
- Some stops are duplicated because of inconsistencies 
  of the GTFS File (e.g. Inbound, Outbound stops).
- Select a stop by clicking on it's name.

Filter stops using the filter box above the stops list.
- Prepend or append your filter with $ to show only 
  stops with fare data 
    - ( e.g. '$ back' to show stops matching 'back' with 
        fares. )
- Use 'red', 'green', 'orange' or 'blue' for specific routes 
    - ('blue' for all blue line stops)

Fares are shown throughout the day in the 
bottom of the screen for a selected stop.
- Fares are grouped into 15 minute increments.

Fares for 8-12-2009 Fare Gate file are loaded by default
- Use the File menu to load other FareGate files. 
    (Only Compressed FareGate files are supported currently)
- Use the Compress a file command to compress a file
  and save it to your hard drive for later use.

For more information, or to report a bug, visit http://mbtafarevis.codeplex.com
  
";
        private const string ABOUT_TEXT =
@"
This application was developed by Jacob Gable (http://jacob4u2.blogspot.com).

This application and it's source code is Licensed under the Microsoft Public License (Ms-PL).

It uses code from the MVVM Light Toolkit V2 and the 
Silverlight Virtual Earth CTP Control.

For more information, or to report a bug, visit http://mbtafarevis.codeplex.com.
";
        public class CompressCommandArgs
        {
            public IGTFSFile FareFile { get; set; }
            public IGTFSFile CompressFile { get; set; }
        }

        public StopsListViewModel StopsList { get; private set; }
        public FareListViewModel FaresList { get; private set; }

        private string testFilesDirectory = "";

        private string stopsFilter;
        public string StopsFilter
        {
            get { return stopsFilter; }
            set
            {
                if (stopsFilter != value)
                {
                    stopsFilter = value;
                    OnPropertyChanged("StopsFilter");
                }
            }
        }

        public DelegateCommand<IGTFSFile> LoadStopFile { get; private set; }
        public DelegateCommand<object> ShowFareFileSelector { get; private set; }
        public DelegateCommand<object> ShowCompressedFareFileSelector { get; private set; }
        public DelegateCommand<object> ShowCompressedFareFileSaver { get; private set; }
        public DelegateCommand<object> ShowCompressFileSelector { get; private set; }
        public DelegateCommand<IGTFSFile> LoadFareFile{ get; private set; }        
        public DelegateCommand<IGTFSFile> LoadCompressedFareFile { get; private set; }
        public DelegateCommand<CompressCommandArgs> CompressFareFile { get; private set; }
        public DelegateCommand<object> ShowBasicHelp { get; private set; }
        public DelegateCommand<object> ShowAbout { get; private set; }        
        public DelegateCommand<KeyEventArgs> FilterOnEnter { get; private set; }
        public DelegateCommand<bool> ToggleShow3DBuildings { get; private set; }

        private IGTFSPackageLoader PackLoader { get; set; }
        private IFareTapLoader fareLoader { get; set; }

        private BackgroundWorker PackageWorker;

        private bool show3DBuildings;
        public bool Show3DBuildings
        {
            get { return show3DBuildings; }
            set
            {
                if (show3DBuildings != value)
                {
                    show3DBuildings = value;
                    OnPropertyChanged("Show3DBuildings");
                }
            }
        }

        private Visibility loadingVis;
        public Visibility LoadingVis
        {
            get { return loadingVis; }
            set
            {
                if (loadingVis != value)
                {
                    loadingVis = value;
                    OnPropertyChanged("LoadingVis");
                }
            }
        }

        private ILocation startupLatLong;
        public ILocation StartupLatLong
        {
            get { return startupLatLong; }
            set
            {
                if (startupLatLong != value)
                {
                    startupLatLong = value;
                    OnPropertyChanged("StartupLatLong");
                }
            }
        }
        private ILocation currLatLong;
        public ILocation CurrLatLong
        {
            get { return currLatLong; }
            set
            {
                if (currLatLong != value)
                {
                    currLatLong = value;
                    OnPropertyChanged("CurrLatLong");
                }
            }
        }

        private double viewAngle;
        public double ViewAngle
        {
            get { return viewAngle; }
            set
            {
                if (viewAngle != value)
                {
                    viewAngle = value;
                    OnPropertyChanged("ViewAngle");
                }
            }
        }


        private IGTFSFile selectedFareFile;
        public IGTFSFile SelectedFareFile
        {
            get { return selectedFareFile; }
            set
            {
                if (selectedFareFile != value)
                {
                    selectedFareFile = value;
                    OnPropertyChanged("SelectedFareFile");
                }
            }
        }

        private IGTFSFile selectedCompressFile;
        public IGTFSFile SelectedCompressFile
        {
            get { return selectedCompressFile; }
            set
            {
                if (selectedCompressFile != value)
                {
                    selectedCompressFile = value;
                    OnPropertyChanged("SelectedCompressFile");
                }
            }
        }

        public ITransitStop LastSelectedStop { get; private set; }
        
        public MainShellViewModel()
        {
            // TODO: Load these from config / settings?
            StartupLatLong = new LocationBase(42.3625, -71.0843, 1000);
            //Show3DBuildings = false;
            CurrLatLong = new LocationBase(42.3625, -71.0843, 1000);

            ViewAngle = 55.00;

            StopsList = new StopsListViewModel();
            FaresList = new FareListViewModel();
            FaresList.CompressedFareFileFinishedLoading += new CompressedFareFileFinishedLoadingEvent(OnFaresListCompressedFareFileFinishedLoading);

            LoadingVis = Visibility.Collapsed;
            
            LoadStopFile = new DelegateCommand<IGTFSFile>(OnLoadStopFile, false);
            
            // Changed because can only show one dialog per request.            
            ShowFareFileSelector = new DelegateCommand<object>(x => SelectFile("Select Fare File...", (y) => SelectedFareFile = y));            
            ShowCompressedFareFileSaver = new DelegateCommand<object>(x => SaveFile("Select Compress File...", (y) => SelectedCompressFile = y));
            
            ShowCompressedFareFileSelector = new DelegateCommand<object>(x => SelectFile("Select Compressed Fare File...", (y) => LoadCompressedFareFile.Execute(y)));
            ShowCompressFileSelector = new DelegateCommand<object>(OnShowCompressFareFile);
                        
            LoadFareFile = new DelegateCommand<IGTFSFile>(OnLoadFareFile, false);
            LoadCompressedFareFile = new DelegateCommand<IGTFSFile>(OnLoadCompressedFareFile, false);
            CompressFareFile = new DelegateCommand<CompressCommandArgs>(OnCompressFareFile, false);
            FilterOnEnter = new DelegateCommand<KeyEventArgs>(OnFilterStopsOnEnter, false);
            ToggleShow3DBuildings = new DelegateCommand<bool>(x => x = !x);

            ShowBasicHelp = new DelegateCommand<object>(x => MessageBox.Show(BASIC_HELP, "Basic Help", MessageBoxButton.OK));
            ShowAbout = new DelegateCommand<object>(x => MessageBox.Show(ABOUT_TEXT, "About MBTA Fare Visualization", MessageBoxButton.OK));

            // Register for stop selected events.
            mediator.Register<StopSelectedMessage>(this, msg => OnStopSelected(msg.Content));

            PackLoader = new PackageLoader();

            PackageWorker = new BackgroundWorker();
            PackageWorker.WorkerReportsProgress = false;
            PackageWorker.WorkerSupportsCancellation = false;
            PackageWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(PackageWorker_RunWorkerCompleted);
            PackageWorker.DoWork += new DoWorkEventHandler(PackageWorker_DoWork);

            // Load the default test data.
            OnLoadTestData(null);
        }

        private void OnShowCompressFareFile(object parm)
        {
            //IGTFSFile fareFile = new GTFSFile("","",null);
            //IGTFSFile compressFile = new GTFSFile("","",null);

            //SelectFile("Select Fare File to Compress...", (y) => fareFile = y);
            //SaveFile("Select File To Save...", (z) => compressFile = z);

            //CompressFareFile.Execute(new CompressCommandArgs() { FareFile = fareFile, CompressFile = compressFile });
        }

        private void OnFaresListCompressedFareFileFinishedLoading(object sender, EventArgs args)
        {
            // Load the last selected stop again to show the updated fare amounts.
            if (this.LastSelectedStop != null)
                OnStopSelected(this.LastSelectedStop);
        }

        private void SelectFile(string title, Action<IGTFSFile> fileAction)
        {
            OpenFileDialog d = new OpenFileDialog();
            //d.Title = title;
            //d.DefaultExt = "txt";
            //d.CheckFileExists = true;
            //d.CheckPathExists = true;
            d.Multiselect = false;

            var result = d.ShowDialog();

            if (!result.HasValue || !result.Value)
                return;

            // Randomizing file names...
            var ran = new Random().Next(99);
            var f = new GTFSFile("ClientLoaded/selectedFile"+ran+".txt", "selectedFile"+ran+".txt", d.File.OpenRead());

            fileAction(f);
        }

        private void SaveFile(string title, Action<IGTFSFile> fileAction)
        {
            SaveFileDialog d = new SaveFileDialog();            
            //d.DefaultExt = "txt";
            //d.CheckFileExists = true;
            //d.CheckPathExists = true;
            //d.Multiselect = false;
            var result = d.ShowDialog();

            if (!result.HasValue || !result.Value)
                return;

            // Randomizing file names...
            var ran = new Random().Next(99);
            var f = new GTFSFile("ClientLoaded/savedFile" + ran + ".txt", "savedFile" + ran + ".txt", d.OpenFile());

            fileAction(f);
        }

        private void OnFilterStopsOnEnter(KeyEventArgs parm)
        {
            // Run the filter stops command on enter key.
            if (parm.Key == Key.Enter)
                StopsList.FilterStops.Execute(StopsFilter);
        }

        //[Cinch.MediatorMessageSink(EventNames.SelectedStop, ParameterType = typeof(ITransitStop))]
        private void OnStopSelected(ITransitStop stop)
        {
            this.CurrLatLong = new LocationBase(stop.Latitude, stop.Longitude, 10);
            LastSelectedStop = stop;

            if (FaresList.LoadedCompressedFares)
                FaresList.ShowFaresForStop.Execute(stop.Name);
        }
        
        private void OnLoadTestData(object parm)
        {   
            PackageWorker.RunWorkerAsync(testFilesDirectory);
        }

        private void PackageWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            e.Result = PackLoader.ParseFiles(new SilverlightFileLoader());
        }

        private void PackageWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {   
            if (e.Error != null)
            {
                MessageBox.Show("Error: " + e.Error.Message);
            }
            
            var files = e.Result as List<IGTFSFile>;
            
            // Process the stops file.
            var stopFile = files.FirstOrDefault(x => x.FileName.Equals("stops.txt", StringComparison.CurrentCultureIgnoreCase));
            if (stopFile != null)
            {   
                LoadStopFile.Execute(stopFile);
            }

            var compressedFareFile = files.FirstOrDefault(x => x.FileName.Contains("faresCompressed"));
            if (compressedFareFile != null)
                LoadCompressedFareFile.Execute(compressedFareFile);
        }

        private ITransitStopLoader stopLoader;
        private void OnLoadStopFile(IGTFSFile stopFile)
        {
            stopLoader = new TransitStopLoader(stopFile);                      
            stopLoader.ItemLoaded += new ItemLoadedEvent<ITransitStop>(OnStopLoaded);
            stopLoader.DoneProcessing += new DoneProcessingEvent(OnStopsFinishedLoading);
            stopLoader.ProcessFile();
        }

        private void OnStopLoaded(object sender, LoadedEventArgs<ITransitStop> args)
        {            
            StopsList.AddStop.Execute(args.Item);
        }

        private void OnStopsFinishedLoading(object sender, DoneProcessingEventArgs args)
        {   
            stopLoader.ItemLoaded -= new ItemLoadedEvent<ITransitStop>(OnStopLoaded);
            stopLoader.DoneProcessing -= new DoneProcessingEvent(OnStopsFinishedLoading);
            stopLoader.Items.Clear();
            stopLoader = null;
        }

        private void OnLoadFareFile(IGTFSFile fareFile)
        {
            // TODO: Signal that we are processing somehow...
            LoadingVis = Visibility.Visible;

            //var newFileName = Path.GetFileNameWithoutExtension(fareFile.FullPath) + "_comp" + Path.GetExtension(fareFile.FullPath);
                        
            //FareFileCompressor.CompressFareFile(fareFile.FullPath, Path.Combine(Path.GetDirectoryName(fareFile.FullPath), newFileName), 
            //    // Callback after compression is done.
            //    x => OnLoadCompressedFareFile(new GTFSFile(new FileInfo(x))));
        }

        private void OnLoadCompressedFareFile(IGTFSFile fareFile)
        {            
            FaresList.LoadCompressedFareFile.Execute(fareFile);
            LoadingVis = Visibility.Collapsed;
        }

        private void OnCompressFareFile(CompressCommandArgs args)
        {
            var fareFile = args.FareFile;
            var compressFile = args.CompressFile;

            LoadingVis = Visibility.Visible;

            //IGTFSFile compressFile = new GTFSFile("", "", null);
            //SaveFile("Select Destination File...", (x) => compressFile = x);

            //mediator.Send<CompressionStartedEvent>(new CompressionStartedEvent(fareFile.Lines.Count()));

            FareFileCompressor.CompressorProgressChanged += new CompressorProgressChangedEvent(OnCompressionProgressChanged);

            FareFileCompressor
                .CompressFareFile(fareFile.BaseStream, 
                        compressFile.BaseStream,
                        x => { OnCompressionCompleted(x); });
        }

        private void OnCompressionProgressChanged(object sender, CompressorProgressChangedEventArgs args)
        {
            //MessageBox.Show("Compression progress changed: " + args.Count);
            mediator.Send<CompressionProgressEvent>(new CompressionProgressEvent(args.Count));
        }

        private void OnCompressionCompleted(Stream x)
        {
            mediator.Send<CompressionCompletedEvent>(new CompressionCompletedEvent(x));
            try
            {
                Deployment.Current.Dispatcher.BeginInvoke(() =>
                    {
                        SelectedCompressFile.Dispose();
                        SelectedFareFile.Dispose();
                        SelectedCompressFile = null;
                        SelectedFareFile = null;
                    });
            }
            catch
            {
                throw new Exception("Problem disposing of selected file resources...");
            }
        }
        
    }
}
